﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Linq;

using Perseus;
using Perseus.Xml;

using Hetymine.Executioner.Commands;

namespace Hetymine.Executioner.Plugins.Commands.Lua {
    public class LuaCommand : ICommand {
        private static LuaCommandConfigurator _Configurator;
        
        public LuaCommand(Lua plugin) {
            this.Plugin = plugin;

            this.CallStrings = new List<string>();
            this.Script = string.Empty;

            this.Delimiter = " ";
            this.Parameters = string.Empty;            
        }

        public Lua Plugin { get; protected set; }

        public string Script { get; set; }
        public string Delimiter { get; set; }
        public string Parameters { get; set; }

        #region ICommand Members
        public string Name { get; set; }
        public bool IsEmpty {
            get {
                if (this.CallStrings.Count < 1 || this.Script.Trim().IsEmpty()) {
                    return true;
                }

                return false;
            }
        }

        public void Load(XElement element) {
            this.Name = element.Element("Name").ValueOrEmpty();

            var callStrings = from s in element.Elements("CallString")
                              select s;

            foreach (string s in callStrings) {
                this.CallStrings.Add(s);
            }

            this.Script = element.Element("Script").ValueOrEmpty();

            this.Delimiter = " ";
            this.Parameters = string.Empty;
            if (element.Element("Params") != null) {
                this.Parameters = element.Element("Params").Value;
                if (element.Element("Params").Attribute("Delimiter") != null) {
                    this.Delimiter = element.Element("Params").Attribute("Delimiter").Value;
                }
            }
        }
        public XElement Save() {
            if (this.IsEmpty) {
                return null;
            }

            XElement xe = new XElement("Lua");
            xe.Add(new XElement("Name", this.Name));

            foreach (string cs in this.CallStrings) {
                xe.Add(new XElement("CallString", cs));
            }
            
            xe.Add(new XElement("Script", this.Script));

            XElement parameters = new XElement("Params", this.Parameters);
            if (this.Delimiter != " ") {
                parameters.Add(new XAttribute("Delimiter", this.Delimiter));
            }
            xe.Add(parameters);

            return xe;
        }

        public List<string> CallStrings { get; set; }
        public ExecutionResult Execute(string parameters) {
            // If the command points directly to a file
            if (File.Exists(this.Script)) {
                try {
                    this.Plugin.Interface.LoadFile(this.Script);
                    return ExecutionResult.Success;
                }
                catch {
                    App.Log("Lua: Failed to load the file \"" + this.Script + "\".");
                }
            }
            else {
                string name = Path.GetFileNameWithoutExtension(this.Script);
                if (this.Plugin.InitializeScript(name)) {
                    parameters = this.InitializeParameters(parameters);
                    return ((bool)this.Plugin.Interface.CallFunction(name + ".execute", parameters)).ToExecutionResult();
                }
            }

            return ExecutionResult.Failure;
        }
        private string InitializeParameters(string parameters) {
            if (this.Parameters != string.Empty) {
                string[] prams;
                if (this.Delimiter == " ") {
                    prams = PString.Split(
                        parameters,
                        this.Delimiter,
                        SplitOptions.Trim | SplitOptions.RemoveEmptyEntries
                    );
                }
                else {
                    prams = PString.Split(parameters, this.Delimiter);
                }
                string new_prams = this.Parameters;
                for (int i = 0; i < prams.Length; i++) {
                    new_prams = new_prams.Replace("{" + i.ToString() + "}", prams[i]);
                }

                parameters = string.Empty;
                int start = 0;
                bool inBracket = false;
                for (int i = 0; i < new_prams.Length - 1; i++) {
                    if (new_prams.Substring(i, 2) == "\\{") {
                        ++i;
                        continue;
                    }

                    if (!inBracket && new_prams.Substring(i, 1) == "{") {
                        parameters += new_prams.Substring(start, i - start);
                        inBracket = true;
                        continue;
                    }

                    if (inBracket && new_prams.Substring(i, 1) == "}") {
                        start = i + 1;
                        inBracket = false;
                    }
                }
                if (!inBracket) {
                    parameters += new_prams.Substring(start);
                }
            }

            return parameters.Trim();
        }

        public ICommandConfigurator<ICommand> Configurator {
            get {
                if (LuaCommand._Configurator == null) {
                    LuaCommand._Configurator = new LuaCommandConfigurator();
                }
                LuaCommand._Configurator.InitializeConfig(this);
                return LuaCommand._Configurator;
            }
        }

        public object Clone() {
            LuaCommand luac = (LuaCommand)this.MemberwiseClone();

            luac.CallStrings = new List<string>();
            foreach (string cs in this.CallStrings) {
                luac.CallStrings.Add(cs);
            }

            return luac;
        }
        #endregion
    }
}
